home *** CD-ROM | disk | FTP | other *** search
- PAGE 60,132
- TITLE BATSTACK -- Special device driver for EBL keyboard stack
- COMMENT $
-
- BATSTACK Nathaniel R. Goodspeed
-
- --------
- Overview
- --------
- This device driver supports the "keyboard stack" available through use of the
- Extended Batch Language, EBL. Normally, the only way to get stuff into the
- keyboard stack is to use the EBL commands STACK or BEGSTACK ... END. (Programs
- remove entries from the stack by attempting normal keyboard reads, and EBL
- provides a way to read the stack into EBL variables.) But BATSTACK allows the
- output of any program to be produced into the stack, using normal DOS output
- redirection:
-
- echo This is going into the keyboard stack>batstack
-
- Obviously, the use of STACK or BEGSTACK is better than ECHOing into BATSTACK.
- But the power of BATSTACK is that any filter can produce output into the
- keyboard stack, for use by any other program, or for further processing by
- EBL programs.
-
- ------------
- Installation
- ------------
- BATSTACK is created by assembling BATSTK.ASM, linking it, and converting it
- to a .COM-form file with the extension .DEV (so nobody tries to execute it
- by typing its name, which would result in chaos).
- masm batstk,batstk,batstk,nul;
- link batstk;
- exe2bin batstk.exe batstk.dev
- del batstk.obj
- del batstk.exe
-
- Once you have BATSTK.DEV, it is installed by adding a line to your CONFIG.SYS
- file of the form:
- DEVICE=BATSTK.DEV
- You must either ensure that BATSTK.DEV is in the root directory of your boot
- disk, or supply the full drive and pathname of its location, e.g.:
- DEVICE=C:\DEVICES\BATSTK.DEV
- BATSTACK will be installed when the system is next rebooted. It will announce
- its presence with the message "BATSTACK driver loaded."
-
- You may be wondering why, since the device is called BATSTACK, the files used
- to implement it are called BATSTK.* . The reason is that once a device with
- a particular name is installed in your system, DOS ignores any extension given
- with that device name! In other words, if the BATSTACK source and device files
- were named BATSTACK.ASM and BATSTACK.DEV, DOS would forget that the disk files
- were there as soon as the BATSTACK device was installed. The command line
- "type batstack.asm" would be treated the same as "type batstack", and would
- cause the contents of the stack to be typed! (This is why it's not possible
- to create a disk file called CON.TXT, too.)
-
- -----------
- Usage Notes
- -----------
- There are two cases in which you might want to redirect a filter's output to
- BATSTACK rather than a file or pipe. The first to let an EBL batch file
- read the output, so that (for instance) a batch file can know a file's size
- without having to display DIR output on the screen and find it using READSCRN.
-
- The second case is when you want the filter to supply some, but not all, of
- the input for a program to follow. When a program thinks it's reading the
- keyboard, it will read the stack until it is exhausted, then revert to reading
- the real keyboard. So to supply the first few commands for program2 from
- program1, you could place in a batch file the lines:
- program1 >batstack
- program2
- By contrast, if you connect the two programs with a DOS pipeline:
- program1 | program2
- or redirect the output of the first into a file, and read that with the second:
- program1 >miscfile
- program2 <miscfile
- then program1 had better generate an explicit command to cause program2 to
- terminate, or else program2 might reach end-of-file on the pipeline or file and
- wait indefinitely for a termination command to appear, without allowing it to
- be typed from the real keyboard! If program2 is itself a filter, or at any
- rate was coded to recognize end-of-file on standard input, it will terminate at
- end-of-file. But in no case will it automatically switch to the real keyboard.
-
- Don't try to copy a large amount of text into BATSTACK, however. The EBL 2.04a
- stack handler apparently isn't very careful about checking for stack-full, at
- least not when text is entered with the INT 16H, AH=73H option. Not only is
- there no defined way to detect a stack-full condition on return from the INT,
- but when I attempted to copy a 537-byte file into a stack initialized with the
- usual BAT * 512 command, the ctrl-PrtSc key stopped working! (DOS echoed ^P
- at the command prompt rather than echoing following characters to the printer.)
-
- --------------------
- Implementation Notes
- --------------------
- The Extended Batch Language STACK and BEGSTACK commands normally delimit each
- line in the stack with a CR, rather than a CR/LF as DOS text lines normally end.
- This is consistent with the pretext that the stack contains user keystrokes;
- the user never types an explicit LF. However, since the BATSTACK driver is
- expected to handle normal DOS text files, it performs a transformation: when
- CR/LF is written to BATSTACK, the LF is discarded; when CR is read from BATSTACK,
- an LF is automatically appended. In theory, unless the text written to BATSTACK
- already contains CR characters without LFs, this should be transparent if the
- BATSTACK driver is both written and read by DOS:
- copy myfile batstack
- copy batstack myfile.new
- But when the stack is read via keyboard requests, no spurious LFs will show up.
-
- Another funny convention followed by BATSTACK when being read as a DOS device
- is that there is always a character ready to be read -- but if the stack is
- empty or disabled, the character returned by BATSTACK will be a simulated
- ctrl-Z, to cause DOS programs to recognize end-of-file. The DOS device driver
- documentation suggests that if a driver sets the "busy" bit meaning that no
- character is currently available, DOS will wait under some circumstances until
- the "busy" bit is reset. We can be almost positive, however, that if the stack
- is empty or disabled, no new characters will appear in it no matter how long
- DOS might wait. This is why we always claim that we have a character ready.
-
- A useful enhancement to the EBL INT 16H stack support would be a status return
- on functions 73H and 74H, so programs could tell if the write failed. Another
- would be a function to request the current stack enable/disable status and the
- length of the current contents. This would let programs specifically interes-
- ted in the stack, not the keyboard, read it even if it were disabled, saving
- and restoring the user's enable/disable state.
-
- NRG 09/05/85
- $
- PAGE
- MYSTACKSIZE EQU 256 ;DOS MANUAL CLAIMS DEVICE DRIVERS NEED OWN STACK
- LF EQU 0AH ;LINE-FEED CHARACTER
- CR EQU 0DH ;CARRIAGE-RETURN CHARACTER
- EOF EQU 1AH ;CTRL-Z IS END-OF-FILE MARKER
-
- LJZ MACRO DEST
- LOCAL NZDEST
- JNZ NZDEST
- JMP DEST
- NZDEST:
- ENDM
-
- DEVATTR RECORD DEVCHAR:1=0, DEVIOCTL:1=0, DEVNONIBM:1=0, UNUSED:9=0, DEVCLOCK:1=0, DEVNUL:1=0, DEVSTDOUT:1=0, DEVSTDIN:1=0
-
- DUMMY STRUC
- WORDSIZE DW ?
- DUMMY ENDS
-
- ; DEVICE HEADER
-
- CSEG SEGMENT BYTE PUBLIC 'CODE'
- DD -1 ;REQUIRED; WILL BECOME PTR TO NEXT DRIVER
- DEVATTR <1,0> ;DEVICE ATTRIBUTES: CHAR, NO IOCTL SUPPORT
- DW OFFSET STRATEGY ;PTR TO DEVICE STRATEGY ROUTINE
- DW OFFSET INTERRUPT ;PTR TO DEVICE INTERRUPT ROUTINE
- DB 'BATSTACK' ;DEVICE NAME -- BETTER BE DIFFERENT FROM
- ;OUR SOURCE, BINARY ETC. OR WE CAN'T ACCESS
- ;DISK FILES ONCE IT'S INSTALLED!
-
- REQHDR DD ? ;SAVE REQUEST HEADER PTR HERE
- MYSP DW -2 ;FIRST CALL, SET SP ALMOST AS HIGH AS CAN REACH
- ;(FOR INIT, WE HAVE ALL OF MEMORY TO OURSELVES)
- OLDSP DW ? ;SAVE DOS SP/SS HERE
- OLDSS DW ?
- LASTREAD DB 00H ;LAST CHARACTER READ FROM STACK
- LASTWRITE DB 00H ;LAST CHARACTER WRITTEN TO STACK
-
- STRATEGY PROC FAR ;ENTER HERE FOR STRATEGY ROUTINE
- ASSUME CS:CSEG ;ONLY CS POINTS TO US
- MOV WORD PTR REQHDR,BX ;SAVE PTR TO REQUEST HEADER
- MOV WORD PTR REQHDR+2,ES
- RET ;SAVED REQUEST HEADER PTR; RETURN
- STRATEGY ENDP
- PAGE
- FUNCTAB EQU THIS WORD ;TABLE OF FUNCTIONS, SUBSCRIPTED BY COMMAND CODE
- DW INIT ; 0 -- INIT
- DW DONE ; 1 -- MEDIA CHECK (NOP FOR CHARACTER DEV)
- DW DONE ; 2 -- BUILD BPB (NOP FOR CHARACTER DEV)
- DW UNKNOWN ; 3 -- IOCTL INPUT
- DW READ ; 4 -- INPUT (READ)
- DW TESTREAD ; 5 -- NON-DESTRUCTIVE INPUT NO WAIT
- DW INSTATUS ; 6 -- INPUT STATUS
- DW INFLUSH ; 7 -- INPUT FLUSH
- DW WRITE ; 8 -- OUTPUT (WRITE)
- DW WRITE ; 9 -- OUTPUT (WRITE) WITH VERIFY
- DW DONE ;10 -- OUTPUT STATUS
- DW DONE ;11 -- OUTPUT FLUSH (WE DON'T BUFFER OUTPUT)
- DW UNKNOWN ;12 -- IOCTL OUTPUT
- FUNCCNT EQU ($-FUNCTAB)/(TYPE WORDSIZE)
-
- INTERRUPT PROC FAR ;HERE TO PERFORM THE REQUEST JUST 'ENQUEUED'
- ASSUME CS:CSEG
- PUSH DS
- PUSH SI
- PUSH ES
- PUSH DI
- PUSH AX
- PUSH BX
- PUSH CX
- MOV AX,CS ;PTR TO THIS SEGMENT
- MOV OLDSP,SP ;SAVE OLD STACK
- MOV OLDSS,SS
- MOV SS,AX ;SET NEW STACK
- MOV SP,MYSP
- LDS SI,REQHDR ;GET FULL PTR TO REQUEST HEADER
- MOV BL,DS:[SI+2] ;GET COMMAND CODE
- CMP BL,FUNCCNT ;CHECK VALIDITY
- JAE UNKNOWN ;NOT RECOGNIZED
- XOR BH,BH ;BX = BL
- SHL BX,1 ;DOUBLE FOR WORD OFFSET INTO FUNCTAB
- CLD ;SET DIRECTION TO 'FORWARD'
- JMP FUNCTAB[BX] ;SWITCH TO APPROPRIATE ROUTINE
- UNKNOWN:
- OR WORD PTR DS:[SI+3],8103H ;SET ERROR, DONE AND 'UNKNOWN COMMAND'
- JMP SHORT EXIT
- DONE:
- OR BYTE PTR DS:[SI+4],01 ;SET DONE BIT
- EXIT:
- MOV SS,OLDSS ;RESTORE ORIGINAL STACK
- MOV SP,OLDSP
- POP CX
- POP BX
- POP AX
- POP DI
- POP ES
- POP SI
- POP DS
- RET
- INTERRUPT ENDP
- PAGE
- READ PROC NEAR ; 4 -- INPUT (READ)
- MOV CX,DS:[SI+18] ;PICK UP BYTE COUNT
- JCXZ DONE ;IGNORE TRIVIAL REQUESTS
- LES DI,DS:[SI+14] ;PICK UP TRANSFER ADDRESS
- READLOOP:
- CMP LASTREAD,CR ;WAS THE LAST CHAR WE READ A CR?
- JNE REALREAD ;IF NOT, READ ANOTHER CHAR
- MOV AL,LF ;LAST CHAR WAS CR: SIMULATE FOLLOWING LF
- JMP SHORT READSTORE
- REALREAD: ;HERE WHEN NOT SIMULATING LF
- MOV AX,7600H ;AH = 76H (CHAR SOURCE TO AL); AL = 00H
- INT 16H ;IF BAT NOT INSTALLED, AL NOT CHANGED
- ;IF BAT INSTALLED, AL = 'K'EYBOARD/'S'TACK
- TEST AL,AL ;CHECK FOR UNCHANGED AL
- JZ UNKNOWN ;IF BAT NOT INSTALLED, WE DON'T SUPPORT FUNCTION
- CMP AL,'S' ;DO WHILE STILL CHARS IN STACK
- JNE READDONE
- XOR AH,AH ;AH = 0: READ NEXT CHAR
- INT 16H ;GET CHAR INTO AL
- READSTORE: ;HERE AL HAS CHAR TO RETURN
- STOSB ;STORE INTO BUFFER, INCREMENT POINTER
- MOV LASTREAD,AL ;SAVE TO CHECK FOR CR/LF SIMULATION
- LOOP READLOOP ;LOOP UNTIL ALL REQUESTED BYTES READ
- JMP SHORT READX ;AND WHEN THAT'S DONE, SKIP TO EXIT
- READDONE: ;HERE WHEN WE CAN'T SATISFY READ REQUEST
- MOV AL,EOF ;GET EOF MARKER
- STOSB ;SHOW IT TO CALLER
- MOV LASTREAD,AL ;KEEP TRACK OF CHARS RETURNED TO CALLER
- DEC CX ;WE SATISFIED ONE MORE REQUESTED CHARACTER
- READX:
- SUB DS:[SI+18],CX ;SUBTRACT CHARS-NOT-READ FROM CHARS-REQUESTED
- ;(THIS RETURNS CHARS-ACTUALLY-READ)
- JMP DONE
- READ ENDP
-
- TESTREAD PROC NEAR ; 5 -- NON-DESTRUCTIVE INPUT NO WAIT
- MOV AX,7600H ;AH = 76H (CHAR SOURCE TO AL); AL = 00H
- INT 16H ;IF BAT NOT INSTALLED, AL NOT CHANGED
- ;IF BAT INSTALLED, AL = 'K'EYBOARD/'S'TACK
- TEST AL,AL ;CHECK FOR UNCHANGED AL
- JZ UNKNOWN ;IF BAT NOT INSTALLED, WE DON'T SUPPORT FUNCTION
- CMP AL,'S' ;IF NO CHARS IN STACK
- JE TEST_READ_STACK
- MOV AL,EOF ;RETURN EOF CHAR IN THAT CASE
- JMP SHORT TESTRET
- TEST_READ_STACK: ;STACK CHAR IS AVAILABLE
- XOR AH,AH ;AH = 0: READ NEXT CHAR FROM STACK
- INT 16H ;GET CHAR INTO AL
- MOV AH,74H ;PUT THAT CHAR BACK ON TOP OF STACK
- INT 16H
- TESTRET:
- MOV DS:[SI+13],AL ;SHOW CHAR TO CALLER
- JMP DONE
- TESTREAD ENDP
- PAGE
- INSTATUS PROC NEAR ; 6 -- INPUT STATUS
- JMP DONE ;NEVER SET 'BUSY' BIT
- INSTATUS ENDP
-
- INFLUSH PROC NEAR ; 7 -- INPUT FLUSH
- MOV AH,77H ;PURGE THE STACK
- INT 16H
- JMP DONE
- INFLUSH ENDP
-
- WRITE PROC NEAR ;8, 9 -- OUTPUT (WRITE) [WITH VERIFY]
- MOV CX,DS:[SI+18] ;PICK UP BYTE COUNT
- JCXZ DONE ;IGNORE TRIVIAL REQUESTS
- MOV AX,7600H ;REQUEST 'K' OR 'S' IN AL, CLEAR AL
- INT 16H ;AL UNCHANGED IF EBL NOT INSTALLED
- TEST AL,AL ;CHECK FOR UNCHANGED AL
- LJZ UNKNOWN ;USELESS TO WRITE TO BATSTACK WITHOUT EBL
- PUSH DS
- PUSH SI
- LDS SI,DS:[SI+14] ;PICK UP TRANSFER ADDRESS
- WRITELOOP:
- LODSB ;GET NEXT CHAR INTO AL
- CMP LASTWRITE,CR ;WAS PREVIOUS CHARACTER CR?
- MOV LASTWRITE,AL ;IN ANY CASE, UPDATE LAST-WRITTEN CHAR
- JNE WRITEOK ;NOT CR, IN WHICH CASE ALLOW EXPLICIT LF
- CMP AL,LF ;LINE FEED: IGNORE THEM (CR/LF --> JUST CR)
- JE NOWRITE
- WRITEOK:
- MOV AH,73H ;STACK AL'S CHAR FIFO STYLE
- INT 16H ;"WRITE" THE CHAR
- ;; WE'D LIKE TO CHECK FOR SUCCESSFUL WRITE, BUT NO STATUS IS DOCUMENTED!
- NOWRITE:
- LOOP WRITELOOP ;LOOP UNTIL ALL REQUESTED BYTES WRITTEN
- POP SI
- POP DS
- SUB DS:[SI+18],CX ;SUBTRACT CHARS-NOT-WRITTEN FROM CHARS-REQUESTED
- ;(THIS RETURNS CHARS-ACTUALLY-WRITTEN)
- JMP DONE
- WRITE ENDP
-
- TOP EQU THIS BYTE ;TRUNCATE HERE AFTER INITIALIZATION
- PAGE
- STARTMSG DB 0DH,0AH,'BATSTACK driver loaded',0DH,0AH,'$'
-
- INIT PROC NEAR ; 0 -- INIT
- PUSH DI
- PUSH ES
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DS ;SAVE AGAIN; WE CHANGE FOR DOS CALL
- PUSH SI
- PUSH DX
- PUSH BP
- MOV AX,CS ;PTR TO CSEG
- MOV DS,AX ;GET DS->CSEG
- MOV AH,9 ;DOS PRINT STRING
- MOV DX,OFFSET STARTMSG ;DS:DX -> MESSAGE
- INT 21H ;ASK DOS TO OUTPUT THE STRING
- POP BP
- POP DX
- POP SI
- POP DS ;RESTORE REQHDR SEG PTR
- POP CX
- POP BX
- POP AX
- POP ES
- POP DI
- MOV WORD PTR DS:[SI+16],CS ;SEGMENT PART OF ENDING ADDRESS
- MOV WORD PTR DS:[SI+14],OFFSET TOP+MYSTACKSIZE ;OFFSET PART OF ENDING ADDR
- MOV MYSP,OFFSET TOP+MYSTACKSIZE ;NEXT ENTRY, USE HIGH END AS STACK
- JMP DONE
- INIT ENDP
-
- CSEG ENDS
- END